package drds.global_transaction.image.image_praser;

import com.google.common.base.Joiner;
import drds.common.Else;
import drds.global_transaction.image.Column;
import drds.global_transaction.image.Image;
import drds.global_transaction.image.Record;
import drds.global_transaction.image.UndoLog;
import lombok.Getter;
import lombok.Setter;
import net.sf.jsqlparser.JSQLParserException;
import org.apache.commons.lang3.StringUtils;
import org.feisoft.common.utils.DbPool.Db;
import org.feisoft.common.utils.SpringBeanUtil;

import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public abstract class AbstractImagePraser implements ImagePraser
{


    //
    @Setter
    @Getter
    protected String originalSql;
    //
    @Setter
    @Getter
    protected String schemaName;
    @Setter
    @Getter
    protected String tableName;
    @Setter
    @Getter
    protected String primaryKeyColumnName;
    //
    @Setter
    @Getter
    protected List<String> columnNameList;
    @Setter
    @Getter
    protected String allColumnNameString;
    @Setter
    @Getter
    protected String whereCondition;
    //
    @Setter
    @Getter
    protected String imageSql;
    @Setter
    @Getter
    protected UndoLog undoLog;
    //
    private Db db = null;

    {
        if (this.db == null)
        {
            Db db = (Db) SpringBeanUtil.getBean("db");
            this.db = db;
        }
    }

    public static AbstractImagePraser newImagePraser(String sql, UndoLog undoLog) throws SQLException
    {
        if (SqlPraser.insert(sql))
        {
            return new InsertImagePraser(sql, undoLog);
        } else if (SqlPraser.update(sql))
        {
            return new UpdateImagePraser(sql, undoLog);
        } else if (SqlPraser.delete(sql))
        {
            return new DeleteImagePraser(sql, undoLog);
        } else if (SqlPraser.select(sql))
        {
            return new SelectImagePraser(sql, undoLog);
        } else
        {
            throw new Else();
        }
    }


    //
    public String getSchemaName() throws SQLException
    {
        Connection connection = db.getConnection();
        DatabaseMetaData databaseMetaData = connection.getMetaData();
        ResultSet schemasResultSet = databaseMetaData.getSchemas(connection.getCatalog(), null);
        String schemasName = "";
        while (schemasResultSet.next())
        {
            schemasName = schemasResultSet.getString("TABLE_SCHEM");
        }
        db.close(connection, null, schemasResultSet);
        return schemasName;
    }

    public String getTableName() throws JSQLParserException, SQLException
    {
        return SqlPraser.tableName(originalSql);
    }

    public String getPrimaryKeyColumnName() throws SQLException
    {
        return getPrimaryKeyColumnName(tableName);
    }

    public String getPrimaryKeyColumnName(String tableName) throws SQLException
    {
        Connection connection = db.getConnection();
        DatabaseMetaData databaseMetaData = connection.getMetaData();
        ResultSet primaryKeyResultSet = databaseMetaData.getPrimaryKeys(connection.getCatalog(), null, tableName);
        String primaryKeyColumnName = "";
        while (primaryKeyResultSet.next())
        {
            primaryKeyColumnName = primaryKeyResultSet.getString("COLUMN_NAME");
        }
        db.close(connection, null, primaryKeyResultSet);

        return primaryKeyColumnName;
    }

    //


    public List<String> getColumnNameList() throws JSQLParserException, SQLException
    {
        Map<String, Object> map = getColumnNameToColumnTypeMap();
        return new ArrayList<>(map.keySet());
    }

    protected Map<String, Object> getColumnNameToColumnTypeMap() throws SQLException
    {
        Connection connection = db.getConnection();
        DatabaseMetaData databaseMetaData = connection.getMetaData();
        ResultSet resultSet = databaseMetaData.getColumns(null, "%", tableName, "%");
        Map<String, Object> map = new HashMap<>();
        while (resultSet.next())
        {
            map.put(resultSet.getString("COLUMN_NAME"), resultSet.getObject("TYPE_NAME"));
        }
        db.close(connection, null, resultSet);
        return map;
    }

    public String getAllColumnNameString()
    {
        return Joiner
                .on(",")
                .skipNulls()
                .join(columnNameList);
    }

    public abstract String getWhereCondition() throws JSQLParserException;

    //

    /**
     * 每次调用的时候都要进行重新生成
     */
    protected Image getImage() throws SQLException, JSQLParserException
    {

        schemaName = getSchemaName();
        tableName = getTableName();
        primaryKeyColumnName = getPrimaryKeyColumnName();
        //
        columnNameList = getColumnNameList();
        allColumnNameString = getAllColumnNameString();
        whereCondition = getWhereCondition();
        //
        Image image = new Image();
        image.setSchemaName(schemaName);
        image.setTableName(tableName);
        //
        getImageSql();
        List<Record> recordList = new ArrayList<Record>();
        List<Map<String, Object>> mapList = db.executeQuery(imageSql, resultSet ->
        {
            Map<String, Object> map = new HashMap<>();
            try
            {
                for (String columnName : columnNameList)
                {
                    map.put(columnName, resultSet.getObject(columnName));
                }
                map.put(primaryKeyColumnName, resultSet.getObject(primaryKeyColumnName));
            } catch (SQLException e)
            {
                e.printStackTrace();
            }
            return map;
        }, null);

        for (Map<String, Object> map : mapList)
        {
            Record record = new Record();
            List<Column> columnList = new ArrayList<Column>();
            for (String columnName : map.keySet())
            {
                Column column = new Column();
                column.setName(columnName);
                column.setType(map.get(columnName).getClass().getName());
                column.setValue(map.get(columnName));
                columnList.add(column);
            }
            record.setColumnList(columnList);
            recordList.add(record);
        }
        image.setRecordList(recordList);
        //
        undoLog.setOriginalSql(originalSql);
        undoLog.setType(originalSql.trim().substring(0, 6));
        undoLog.setPrimaryKeyColumnName(primaryKeyColumnName);
        undoLog.setSelectBody("select " + primaryKeyColumnName + "," + allColumnNameString + " from " + tableName);
        undoLog.setSelectWhere(" where 1=1 and " + whereCondition);


        return image;
    }

    protected void getImageSql()
    {
        StringBuffer sb = new StringBuffer("select ");
        sb.append(primaryKeyColumnName + "," + allColumnNameString);
        sb.append(" from ");
        sb.append(tableName);
        if (StringUtils.isNotBlank(whereCondition))
        {
            sb.append(" where ");
            sb.append(whereCondition);
        }
        imageSql = sb.toString();
    }


    abstract public String getBeforeOrAfterImageSql() throws JSQLParserException, SQLException;
}
